/*
 * Change Logs:
 * Date           Author       Notes
 * 2022-03-27     chenbin
 */
#include "rbl_unpack.h"
#if (FBOOT_USING_FUPDATE == 1)

#define DBG_ENABLE
#define DBG_SECTION_NAME "fupdate"
#define DBG_LEVEL DBG_INFO
#include <rtdbg.h>

#include "fal.h"

rbl_unpack_context_t* rbl_unpack_context_create(void)
{
	rbl_unpack_context_t * rbl_unpack_ctx = NULL;
	
	rbl_unpack_ctx = rt_calloc(1, sizeof(rbl_unpack_context_t));
	
	return rbl_unpack_ctx;
}

void rbl_unpack_context_delete(rbl_unpack_context_t* unpack_ctx)
{
	rt_free(unpack_ctx);
}

void rbl_unpack_header_show(rbl_header_info_t *p_header)
{
	rt_uint32_t timestamp = 0;
	char put_buf[24] = {0};
	rt_memset(put_buf, 0x0, sizeof(put_buf));
	if ((p_header->fota_algo & RBL_CRYPT_STAT_MASK) == RBL_CRYPT_ALGO_AES256)
	{
		rt_strncpy(put_buf, "AES", 3);
	}
	else if ((p_header->fota_algo & RBL_CRYPT_STAT_MASK) == RBL_CRYPT_ALGO_XOR)
	{
		rt_strncpy(put_buf, "XOR", 3);
	}
	else
	{
		rt_strncpy(put_buf, "NONE", 4);
	}
	if ((p_header->fota_algo & RBL_CMPRS_STAT_MASK) == RBL_CMPRS_ALGO_GZIP)
	{
		rt_strncpy(&put_buf[rt_strlen(put_buf)], " && GZIP", 8);
	}
	else if ((p_header->fota_algo & RBL_CMPRS_STAT_MASK) == RBL_CMPRS_ALGO_QUICKLZ)
	{
		rt_strncpy(&put_buf[rt_strlen(put_buf)], " && QUICKLZ", 11);
	}
	else if ((p_header->fota_algo & RBL_CMPRS_STAT_MASK) == RBL_CMPRS_ALGO_FASTLZ)
	{
		rt_strncpy(&put_buf[rt_strlen(put_buf)], " && FASTLZ", 10);
	}
	if (rt_strlen(put_buf) <= 0)
	{
		rt_strncpy(put_buf, "NONE", 4);
	}
	timestamp = p_header->fm_time[2] + (p_header->fm_time[3] << 8) + (p_header->fm_time[4] << 16) + (p_header->fm_time[5] << 24);
	rt_kprintf("============== RBL header ====================\n");
	rt_kprintf("| Pack algorithm   | %s \n",  put_buf);
	rt_kprintf("| Pack timestamp   | %010d \n", timestamp);
	rt_kprintf("| Target partition | %s \n", p_header->app_part_name);
	rt_kprintf("| Download version | %s \n", p_header->download_version);
	rt_kprintf("| Current  version | %s \n", p_header->current_version);
	rt_kprintf("| Body CRC32       | 0x%08X \n", p_header->body_crc);
	rt_kprintf("| HASH CODE        | 0x%08X \n", p_header->hash_val);
	rt_kprintf("| Body RAW size    | %010d \n", p_header->raw_size);
	rt_kprintf("| Body PKG size    | %010d \n", p_header->pkg_size);
	rt_kprintf("| Header CRC32     | 0x%08X \n", p_header->header_crc);
	rt_kprintf("==============================================\n");
}


int rbl_unpack_header_check(rbl_header_info_t *p_header)
{
	int ret = 1;
	uint32_t hdr_crc = 0;
	if(p_header == NULL)
	{
		ret = RBL_UNPACK_ERR_GENERAL;
		goto __exit;
	}
	rbl_crc32_init(); // crc32 init
	hdr_crc = rbl_crc32((rt_uint8_t *)p_header, sizeof(rbl_header_info_t) - 4);
	if (hdr_crc != p_header->header_crc)
	{
		LOG_E("header crc 0x%08X != 0x%08X.", hdr_crc, p_header->header_crc);
		ret = RBL_UNPACK_ERR_CHECK;
		goto __exit;
	}
	if (rt_strcmp(p_header->type, "RBL") != 0)
	{
		ret = RBL_UNPACK_ERR_CHECK;
		goto __exit;
	}
	ret = 1;
__exit:
	return ret;
}


int rbl_unpack_context_buffer_init(rbl_unpack_context_t* unpack_ctx)
{
	int ret = RBL_UNPACK_ERR_NO;

	if (unpack_ctx->rw_buffer == RT_NULL)
	{
		unpack_ctx->rw_buffer = rt_malloc(RBL_UNPACK_RW_BUFFER_SIZE);
		if (unpack_ctx->rw_buffer == RT_NULL)
		{
			ret = RBL_UNPACK_ERR_NO_MEM;
			goto __exit;
		}
	}
	/* AES256 algorithm enable */
	if ((unpack_ctx->rbl_header.fota_algo & RBL_CRYPT_STAT_MASK) == RBL_CRYPT_ALGO_AES256)
	{
#if (RBL_UNPACK_USING_AES256 == 1)
		if (unpack_ctx->decrypt_buffer == RT_NULL)
		{
			unpack_ctx->decrypt_buffer = rt_malloc(RBL_UNPACK_RW_BUFFER_SIZE);
			if (unpack_ctx->decrypt_buffer == RT_NULL)
			{
				ret = RBL_UNPACK_ERR_NO_MEM;
				goto __exit;
			}
		}
		if (unpack_ctx->decrypt_ctx == RT_NULL)
		{
			unpack_ctx->decrypt_ctx = rbl_aes256_decrypt_context_create();
			if (unpack_ctx->decrypt_ctx == RT_NULL)
			{
				ret = RBL_UNPACK_ERR_NO_MEM;
				goto __exit;
			}
			unpack_ctx->decrypt_key =  fupdate_aes256_key_get();
			unpack_ctx->decrypt_iv =  fupdate_aes256_iv_get();
			rbl_aes256_decrypt_init_keyiv( unpack_ctx->decrypt_ctx, unpack_ctx->decrypt_key, unpack_ctx->decrypt_iv); // get aes256 key
		}
#else
		LOG_E("Not surpport aes256.");
		ret = RBL_UNPACK_ERR_GENERAL;
		goto __exit;
#endif
	}
	else if ((unpack_ctx->rbl_header.fota_algo & RBL_CRYPT_STAT_MASK) == RBL_CRYPT_ALGO_XOR)
	{
		LOG_E("Not surpport xor.");
		ret = RBL_UNPACK_ERR_GENERAL;
		goto __exit;
	}

	/* If enable fastlz compress function */
	if ((unpack_ctx->rbl_header.fota_algo & RBL_CMPRS_STAT_MASK) == RBL_CMPRS_ALGO_FASTLZ)
	{
#if (RBL_UNPACK_USING_FASTLZ == 1)
		if (unpack_ctx->compress_buffer == RT_NULL)
		{
			unpack_ctx->compress_buffer = rt_malloc(RBL_UNPACK_COMPRESS_BUFFER_SIZE);
			if (unpack_ctx->compress_buffer == RT_NULL)
			{
				ret = RBL_UNPACK_ERR_NO_MEM;
				goto __exit;
			}
		}
		if (unpack_ctx->decompress_fz_ctx == RT_NULL)
		{
			unpack_ctx->decompress_fz_ctx = rbl_fastlz_decompress_context_create();  // fastlz_decompress_context
			if (unpack_ctx->decompress_fz_ctx == RT_NULL)
			{
				ret = RBL_UNPACK_ERR_NO_MEM;
				goto __exit;
			}
		}
#else
		LOG_E("Not surpport fastlz.");
		ret = RBL_UNPACK_ERR_GENERAL;
		goto __exit;
#endif
	}
	else if ((unpack_ctx->rbl_header.fota_algo & RBL_CMPRS_STAT_MASK) == RBL_CMPRS_ALGO_QUICKLZ)
	{
#if (RBL_UNPACK_USING_QUICKLZ == 1)
		if (unpack_ctx->compress_buffer == RT_NULL)
		{
			unpack_ctx->compress_buffer = rt_malloc(RBL_UNPACK_COMPRESS_BUFFER_SIZE);
			if (unpack_ctx->compress_buffer == RT_NULL)
			{
				ret = RBL_UNPACK_ERR_NO_MEM;
				goto __exit;
			}
		}
		if (unpack_ctx->decompress_qz_ctx == RT_NULL)
		{
			unpack_ctx->decompress_qz_ctx = rbl_quicklz_decompress_context_create();  // quicklz_decompress_context
			if (unpack_ctx->decompress_qz_ctx == RT_NULL)
			{
				ret = RBL_UNPACK_ERR_NO_MEM;
				goto __exit;
			}
		}
#else
		LOG_E("Not surpport quicklz.");
		ret = RBL_UNPACK_ERR_GENERAL;
		goto __exit;
#endif
	}
	else if ((unpack_ctx->rbl_header.fota_algo & RBL_CMPRS_STAT_MASK) == RBL_CMPRS_ALGO_GZIP)
	{
#if (RBL_UNPACK_USING_GZIP == 1)
		if (unpack_ctx->compress_buffer == RT_NULL)
		{
			unpack_ctx->compress_buffer = rt_malloc(RBL_UNPACK_COMPRESS_BUFFER_SIZE);
			if (unpack_ctx->compress_buffer == RT_NULL)
			{
				ret = RBL_UNPACK_ERR_NO_MEM;
				goto __exit;
			}
		}
		if (unpack_ctx->decompress_gzip_ctx == RT_NULL)
		{
			unpack_ctx->decompress_gzip_ctx = rbl_gzip_decompress_context_create();  // quicklz_decompress_context
			if (unpack_ctx->decompress_gzip_ctx == RT_NULL)
			{
				ret = RBL_UNPACK_ERR_NO_MEM;
				goto __exit;
			}
		}
#else
		LOG_E("Not surpport gzip.");
		ret = RBL_UNPACK_ERR_GENERAL;
		goto __exit;
#endif
	}
__exit:
	
	if(ret == RBL_UNPACK_ERR_NO_MEM)
	{
		LOG_E("Not enough memory for buffer.");
	}
	return ret;
}	

int rbl_unpack_context_buffer_deinit(rbl_unpack_context_t* unpack_ctx)
{
	if (unpack_ctx->rw_buffer != NULL)
	{
		rt_free(unpack_ctx->rw_buffer);
		unpack_ctx->rw_buffer = NULL;
	}
	if (unpack_ctx->compress_buffer != NULL)
	{
		rt_free(unpack_ctx->compress_buffer);
		unpack_ctx->compress_buffer = NULL;
	}
	if (unpack_ctx->decrypt_buffer != NULL)
	{
		rt_free(unpack_ctx->decrypt_buffer);
		unpack_ctx->decrypt_buffer = NULL;
	}
#if (RBL_UNPACK_USING_AES256 == 1)	
	if (unpack_ctx->decrypt_ctx != NULL)
	{
		rbl_aes256_decrypt_context_delete(unpack_ctx->decrypt_ctx);
		unpack_ctx->decrypt_ctx = NULL;
	}
#endif
#if (RBL_UNPACK_USING_FASTLZ == 1)	
	if (unpack_ctx->decompress_fz_ctx != NULL)
	{
		rbl_fastlz_decompress_context_delete(unpack_ctx->decompress_fz_ctx);
		unpack_ctx->decompress_fz_ctx = NULL;
	}
#endif
#if (RBL_UNPACK_USING_QUICKLZ == 1)	
	if (unpack_ctx->decompress_qz_ctx != NULL)
	{
		rbl_quicklz_decompress_context_delete(unpack_ctx->decompress_qz_ctx);
		unpack_ctx->decompress_qz_ctx = NULL;
	}
#endif
#if (RBL_UNPACK_USING_GZIP == 1)	
	if (unpack_ctx->decompress_gzip_ctx != NULL)
	{
		rbl_gzip_decompress_context_delete(unpack_ctx->decompress_gzip_ctx);
		unpack_ctx->decompress_gzip_ctx = NULL;
	}
#endif
	return 1;
}

int rbl_unpack_header_version_check(rbl_unpack_context_t* unpack_ctx)
{
	int ret = 0;
	rbl_header_info_t *p_header = &(unpack_ctx->rbl_header);
	if(p_header == NULL)
	{
		ret = 0;
		goto __exit;
	}
	ret = rt_strncmp(p_header->download_version, p_header->current_version, 24);
	LOG_I("check version  rc: %d", ret);
__exit:
	return ret;
}

int rbl_unpack_header_version_update(rbl_unpack_context_t* unpack_ctx)
{
	int ret = 1;
	rbl_header_info_t *p_header = NULL;
	
	rt_memset(unpack_ctx->rw_buffer, 0, RBL_UNPACK_RW_BUFFER_SIZE);
	
	ret = fstorage_read(unpack_ctx->src_interface, 0, unpack_ctx->rw_buffer, RBL_UNPACK_RW_BUFFER_SIZE);
	if (ret <= 0)
	{
		ret = RBL_UNPACK_ERR_READ;
		goto __exit;
	}
	rbl_crc32_init(); // crc32 init
	
	p_header = (rbl_header_info_t *)unpack_ctx->rw_buffer;
	
	rt_memcpy(p_header->current_version, p_header->download_version, sizeof(p_header->current_version));
	LOG_I("header crc32 old: 0x%08X ", p_header->header_crc);

	p_header->header_crc = rbl_crc32(unpack_ctx->rw_buffer, sizeof(rbl_header_info_t) - 4);
	LOG_I("header crc32 new: 0x%08X ", p_header->header_crc);
	
	LOG_I("erase len: 0x%08X.",RBL_UNPACK_RW_BUFFER_SIZE);
	ret = fstorage_erase(unpack_ctx->src_interface, 0, RBL_UNPACK_RW_BUFFER_SIZE);
	if (ret < 0)
	{
		goto __exit;
	}
	ret = fstorage_write(unpack_ctx->src_interface, 0, unpack_ctx->rw_buffer, RBL_UNPACK_RW_BUFFER_SIZE);
	if (ret < 0)
	{
		goto __exit;
	}
	ret = 1;
__exit:
	return ret;
}

int rbl_unpack_body_check(rbl_unpack_context_t* unpack_ctx)
{
	rt_uint32_t body_crc = 0xffffffff;

	int ret = 1;
	// int target_write_rc = 0;
	int target_write_pos = 0;
	int target_write_len = 0;
	int target_remain_len = 0;
	int src_read_rc = 0;
	int src_read_pos = 0;
	int src_read_len = 0;
	int src_remain_len = 0;

	uint32_t pkg_size = unpack_ctx->rbl_header.pkg_size;
	uint32_t raw_size = unpack_ctx->rbl_header.raw_size;

	rbl_crc32_init(); // crc32 init
	LOG_I("start check data...");

	LOG_RAW("check < %02d%% >", src_read_pos * 100 / pkg_size);
	while (src_read_pos < pkg_size)
	{
		src_read_len = RBL_UNPACK_RW_BUFFER_SIZE;
		src_remain_len = (pkg_size - src_read_pos);
		if (src_read_len > src_remain_len)
		{
			src_read_len = src_remain_len;
		}
		src_read_rc = fstorage_read(unpack_ctx->src_interface, sizeof(rbl_header_info_t) + src_read_pos, unpack_ctx->rw_buffer, src_read_len);
		if (src_read_rc <= 0)
		{
			ret = RBL_UNPACK_ERR_READ;
			goto __exit;
		}
		src_read_pos += src_read_rc;

		target_write_len = src_read_rc; //
		target_remain_len = (pkg_size - target_write_pos);
		if (target_write_len > target_remain_len)
		{
			target_write_len = target_remain_len;
		}
		body_crc = rbl_step_crc32(body_crc, unpack_ctx->rw_buffer, target_write_len);
		target_write_pos += target_write_len;

		LOG_RAW("\r\033[K");
		LOG_RAW("check < %02d%% >", target_write_pos * 100 / raw_size);
	}
	LOG_RAW("\r\033[K");
	LOG_RAW("check < %02d%% >\n", src_read_pos * 100 / pkg_size);
	LOG_I("read  %08d / %08d bytes.", src_read_pos, pkg_size);
	LOG_I("check %08d / %08d bytes.", target_write_pos, raw_size);

	body_crc ^= 0xffffffff;
	
	LOG_I("rbl  crc 0x%08X.", unpack_ctx->rbl_header.body_crc);
	LOG_I("body crc 0x%08X.", body_crc);
	if (body_crc != unpack_ctx->rbl_header.body_crc)
	{
		ret = RBL_UNPACK_ERR_CHECK;
	}
__exit:
	LOG_I("check status:%d.",ret);
	return ret;
}




#define STR_SHOW_UNPACK_RATE  "unpack < %02d%% > "


#if (RBL_UNPACK_USING_AES256 == 1)	

#if (RBL_UNPACK_USING_GZIP == 1)

int rbl_unpack_body_output_aes256_gzip(rbl_unpack_context_t* unpack_ctx)
{
	int ret = 0;
	int target_write_rc = 0;
	int target_write_len = 0;
	int target_remain_len = 0;
	int src_read_rc = 0;
	int src_read_len = 0;
	int src_remain_len = 0;
	
	uint32_t compress_remain = 0;
	//uint32_t compress_block_size = 0;
	uint32_t compress_rc = 0;
	
	LOG_I("unpack aes256 gzip.");
	LOG_RAW(STR_SHOW_UNPACK_RATE, unpack_ctx->src_read_pos * 100 / unpack_ctx->src_read_total);
	while (unpack_ctx->src_read_pos < unpack_ctx->src_read_total)
	{
		src_read_len = RBL_UNPACK_RW_BUFFER_SIZE;
		src_remain_len = (unpack_ctx->src_read_total - unpack_ctx->src_read_pos);
		if (src_read_len > src_remain_len)
		{
			src_read_len = src_remain_len;
		}
		rt_memset(unpack_ctx->rw_buffer, 0, RBL_UNPACK_RW_BUFFER_SIZE);
		src_read_rc = fstorage_read(unpack_ctx->src_interface, sizeof(rbl_header_info_t) + unpack_ctx->src_read_pos, unpack_ctx->rw_buffer, src_read_len);
		if (src_read_rc <= 0)
		{
			ret = RBL_UNPACK_ERR_READ;
			goto __exit;
		}
		unpack_ctx->src_read_pos += src_read_rc;
		
		rt_memset(unpack_ctx->decrypt_buffer, 0, RBL_UNPACK_RW_BUFFER_SIZE);
		rbl_aes256_decrypt_data(unpack_ctx->decrypt_ctx, unpack_ctx->rw_buffer, unpack_ctx->decrypt_buffer, src_read_rc); // decrypt
		
		if( (compress_remain + src_read_rc) > RBL_UNPACK_COMPRESS_BUFFER_SIZE)
		{
			LOG_E("compress buffer len:%d < (%d + %d).", RBL_UNPACK_COMPRESS_BUFFER_SIZE, compress_remain, src_read_rc);
			ret = RBL_UNPACK_ERR_CHECK;
			goto __exit;
		}
		rt_memcpy(unpack_ctx->compress_buffer + compress_remain, unpack_ctx->decrypt_buffer, src_read_rc); // decrypt_buffer >> compress_buffer
		compress_remain += src_read_rc;
		
		rbl_gzip_decompress_set_in(unpack_ctx->decompress_gzip_ctx, unpack_ctx->compress_buffer, compress_remain);
		while (1)
		{
			compress_rc = rbl_gzip_decompress_data(unpack_ctx->decompress_gzip_ctx, unpack_ctx->rw_buffer, RBL_UNPACK_RW_BUFFER_SIZE);
			if (compress_rc <= 0)
			{
				compress_remain = 0;
				break;
			}
			target_write_len = compress_rc;
			target_remain_len = (unpack_ctx->target_write_total - unpack_ctx->target_write_pos);
			if (target_write_len > target_remain_len)
			{
				target_write_len = target_remain_len;
			}
			target_write_rc = fstorage_write(unpack_ctx->target_interface, unpack_ctx->target_write_pos, unpack_ctx->rw_buffer, target_write_len);
			if (target_write_rc < 0)
			{
				ret = RBL_UNPACK_ERR_WRITE;
				goto __exit;
			}
			unpack_ctx->target_write_pos += target_write_rc;
		}
		LOG_RAW("\r\033[K");
		LOG_RAW(STR_SHOW_UNPACK_RATE, unpack_ctx->src_read_pos * 100 / unpack_ctx->src_read_total);
	}
	LOG_RAW("\r\033[K");
	LOG_RAW(STR_SHOW_UNPACK_RATE, unpack_ctx->src_read_pos * 100 / unpack_ctx->src_read_total);
	LOG_RAW("\n");
	
	ret = unpack_ctx->target_write_pos;
	
__exit:
	return ret;
}
#endif /* RBL_UNPACK_USING_GZIP */


#if (RBL_UNPACK_USING_FASTLZ == 1)

int rbl_unpack_body_output_aes256_fastlz(rbl_unpack_context_t* unpack_ctx)
{
	int ret = 0;
	int target_write_rc = 0;
	int target_write_len = 0;
	int target_remain_len = 0;
	int src_read_rc = 0;
	int src_read_len = 0;
	int src_remain_len = 0;
	
	uint32_t compress_remain = 0;
	uint32_t compress_block_size = 0;
	uint32_t compress_rc = 0;
	
	LOG_I("unpack aes256 fastlz.");
	LOG_RAW(STR_SHOW_UNPACK_RATE, unpack_ctx->src_read_pos * 100 / unpack_ctx->src_read_total);
	while (unpack_ctx->src_read_pos < unpack_ctx->src_read_total)
	{
		src_read_len = RBL_UNPACK_RW_BUFFER_SIZE;
		src_remain_len = (unpack_ctx->src_read_total - unpack_ctx->src_read_pos);
		if (src_read_len > src_remain_len)
		{
			src_read_len = src_remain_len;
		}
		rt_memset(unpack_ctx->rw_buffer, 0, RBL_UNPACK_RW_BUFFER_SIZE);
		src_read_rc = fstorage_read(unpack_ctx->src_interface, sizeof(rbl_header_info_t) + unpack_ctx->src_read_pos, unpack_ctx->rw_buffer, src_read_len);
		if (src_read_rc <= 0)
		{
			ret = RBL_UNPACK_ERR_READ;
			goto __exit;
		}
		unpack_ctx->src_read_pos += src_read_rc;
		
		rt_memset(unpack_ctx->decrypt_buffer, 0, RBL_UNPACK_RW_BUFFER_SIZE);
		rbl_aes256_decrypt_data(unpack_ctx->decrypt_ctx, unpack_ctx->rw_buffer, unpack_ctx->decrypt_buffer, src_read_rc); // decrypt
		
		if( (compress_remain + src_read_rc) > RBL_UNPACK_COMPRESS_BUFFER_SIZE)
		{
			LOG_E("compress buffer len:%d < (%d + %d).", RBL_UNPACK_COMPRESS_BUFFER_SIZE, compress_remain, src_read_rc);
			ret = RBL_UNPACK_ERR_CHECK;
			goto __exit;
		}
		rt_memcpy(unpack_ctx->compress_buffer + compress_remain, unpack_ctx->decrypt_buffer, src_read_rc); // decrypt_buffer >> compress_buffer
		compress_remain += src_read_rc;
		while (1)
		{
			compress_block_size = rbl_fastlz_decompress_get_block_size(unpack_ctx->decompress_fz_ctx, unpack_ctx->compress_buffer);
			if (compress_block_size <= 0)
			{
				break;
			}
			if ((compress_block_size + FUPDATE_FASTLZ_HEADER_SIZE) > compress_remain)
			{
				break;
			}
			compress_rc = rbl_fastlz_decompress_data(unpack_ctx->decompress_fz_ctx, unpack_ctx->rw_buffer, RBL_UNPACK_RW_BUFFER_SIZE, &(unpack_ctx->compress_buffer[FUPDATE_FASTLZ_HEADER_SIZE]), compress_block_size);
			if (compress_rc <= 0)
			{
				LOG_E("fastlz decompress failed. code:%d", compress_rc);
				ret = RBL_UNPACK_ERR_WRITE;
				goto __exit;
			}
			target_write_len = compress_rc;
			target_remain_len = (unpack_ctx->target_write_total - unpack_ctx->target_write_pos);
			if (target_write_len > target_remain_len)
			{
				target_write_len = target_remain_len;
			}
			target_write_rc = fstorage_write(unpack_ctx->target_interface, unpack_ctx->target_write_pos, unpack_ctx->rw_buffer, target_write_len);
			if (target_write_rc < 0)
			{
				ret = RBL_UNPACK_ERR_WRITE;
				goto __exit;
			}
			unpack_ctx->target_write_pos += target_write_rc;

			compress_remain = compress_remain - (compress_block_size + FUPDATE_FASTLZ_HEADER_SIZE);
			if (compress_remain > 0)
			{
				rt_memcpy(unpack_ctx->compress_buffer, &(unpack_ctx->compress_buffer[(compress_block_size + FUPDATE_FASTLZ_HEADER_SIZE)]), compress_remain);
			}
		}
		LOG_RAW("\r\033[K");
		LOG_RAW(STR_SHOW_UNPACK_RATE, unpack_ctx->src_read_pos * 100 / unpack_ctx->src_read_total);
	}
	LOG_RAW("\r\033[K");
	LOG_RAW(STR_SHOW_UNPACK_RATE, unpack_ctx->src_read_pos * 100 / unpack_ctx->src_read_total);
	LOG_RAW("\n");
	
	ret = unpack_ctx->target_write_pos;
	
__exit:
	return ret;
}
#endif /* RBL_UNPACK_USING_FASTLZ */


#if (RBL_UNPACK_USING_QUICKLZ == 1)

int rbl_unpack_body_output_aes256_quicklz(rbl_unpack_context_t* unpack_ctx)
{
	int ret = 0;
	int target_write_rc = 0;
	int target_write_len = 0;
	int target_remain_len = 0;
	int src_read_rc = 0;
	int src_read_len = 0;
	int src_remain_len = 0;
	
	uint32_t compress_remain = 0;
	uint32_t compress_block_size = 0;
	uint32_t compress_rc = 0;
	
	LOG_I("unpack aes256 quicklz.");
	LOG_RAW(STR_SHOW_UNPACK_RATE, unpack_ctx->src_read_pos * 100 / unpack_ctx->src_read_total);
	while (unpack_ctx->src_read_pos < unpack_ctx->src_read_total)
	{
		src_read_len = RBL_UNPACK_RW_BUFFER_SIZE;
		src_remain_len = (unpack_ctx->src_read_total - unpack_ctx->src_read_pos);
		if (src_read_len > src_remain_len)
		{
			src_read_len = src_remain_len;
		}
		rt_memset(unpack_ctx->rw_buffer, 0, RBL_UNPACK_RW_BUFFER_SIZE);
		src_read_rc = fstorage_read(unpack_ctx->src_interface, sizeof(rbl_header_info_t) + unpack_ctx->src_read_pos, unpack_ctx->rw_buffer, src_read_len);
		if (src_read_rc <= 0)
		{
			ret = RBL_UNPACK_ERR_READ;
			goto __exit;
		}
		unpack_ctx->src_read_pos += src_read_rc;
		
		rt_memset(unpack_ctx->decrypt_buffer, 0, RBL_UNPACK_RW_BUFFER_SIZE);
		rbl_aes256_decrypt_data(unpack_ctx->decrypt_ctx, unpack_ctx->rw_buffer, unpack_ctx->decrypt_buffer, src_read_rc); // decrypt
		
		if( (compress_remain + src_read_rc) > RBL_UNPACK_COMPRESS_BUFFER_SIZE)
		{
			LOG_E("compress buffer len:%d < (%d + %d).", RBL_UNPACK_COMPRESS_BUFFER_SIZE, compress_remain, src_read_rc);
			ret = RBL_UNPACK_ERR_CHECK;
			goto __exit;
		}
		rt_memcpy(unpack_ctx->compress_buffer + compress_remain, unpack_ctx->decrypt_buffer, src_read_rc); // decrypt_buffer >> compress_buffer
		compress_remain += src_read_rc;
		while (1)
		{
			compress_block_size = rbl_quicklz_decompress_get_block_size(unpack_ctx->decompress_qz_ctx,  unpack_ctx->compress_buffer);
			if (compress_block_size <= 0)
			{
				break;
			}
			if ((compress_block_size + FUPDATE_QUICKLZ_HEADER_SIZE) > compress_remain)
			{
				break;
			}
			compress_rc = rbl_quicklz_decompress_data(unpack_ctx->decompress_qz_ctx, unpack_ctx->rw_buffer, &(unpack_ctx->compress_buffer[FUPDATE_QUICKLZ_HEADER_SIZE]));
			if (compress_rc <= 0)
			{
				LOG_E("quicklz decompress failed. code:%d", compress_rc);
				ret = RBL_UNPACK_ERR_WRITE;
				goto __exit;
			}
			target_write_len = compress_rc;
			target_remain_len = (unpack_ctx->target_write_total - unpack_ctx->target_write_pos);
			if (target_write_len > target_remain_len)
			{
				target_write_len = target_remain_len;
			}
			target_write_rc = fstorage_write(unpack_ctx->target_interface, unpack_ctx->target_write_pos, unpack_ctx->rw_buffer, target_write_len);
			if (target_write_rc < 0)
			{
				ret = RBL_UNPACK_ERR_WRITE;
				goto __exit;
			}
			unpack_ctx->target_write_pos += target_write_rc;

			compress_remain = compress_remain - (compress_block_size + FUPDATE_QUICKLZ_HEADER_SIZE);
			if (compress_remain > 0)
			{
				rt_memcpy(unpack_ctx->compress_buffer, &(unpack_ctx->compress_buffer[(compress_block_size + FUPDATE_QUICKLZ_HEADER_SIZE)]), compress_remain);
			}
		}
		LOG_RAW("\r\033[K");
		LOG_RAW(STR_SHOW_UNPACK_RATE, unpack_ctx->src_read_pos * 100 / unpack_ctx->src_read_total);
	}
	LOG_RAW("\r\033[K");
	LOG_RAW(STR_SHOW_UNPACK_RATE, unpack_ctx->src_read_pos * 100 / unpack_ctx->src_read_total);
	LOG_RAW("\n");
	
	ret = unpack_ctx->target_write_pos;
	
__exit:
	return ret;
}

#endif /* RBL_UNPACK_USING_QUICKLZ */

int rbl_unpack_body_output_aes256_none(rbl_unpack_context_t* unpack_ctx)
{	
	int ret = 0;
	int target_write_rc = 0;
	int target_write_len = 0;
	int target_remain_len = 0;
	int src_read_rc = 0;
	int src_read_len = 0;
	int src_remain_len = 0;
	
	LOG_I("unpack aes256 none.");
	LOG_RAW(STR_SHOW_UNPACK_RATE, unpack_ctx->src_read_pos * 100 / unpack_ctx->src_read_total);
	while (unpack_ctx->src_read_pos < unpack_ctx->src_read_total)
	{
		src_read_len = RBL_UNPACK_RW_BUFFER_SIZE;
		src_remain_len = (unpack_ctx->src_read_total - unpack_ctx->src_read_pos);
		if (src_read_len > src_remain_len)
		{
			src_read_len = src_remain_len;
		}
		rt_memset(unpack_ctx->rw_buffer, 0, RBL_UNPACK_RW_BUFFER_SIZE);
		src_read_rc = fstorage_read(unpack_ctx->src_interface, sizeof(rbl_header_info_t) + unpack_ctx->src_read_pos, unpack_ctx->rw_buffer, src_read_len);
		if (src_read_rc <= 0)
		{
			ret = RBL_UNPACK_ERR_READ;
			goto __exit;
		}
		unpack_ctx->src_read_pos += src_read_rc;

		rt_memset(unpack_ctx->decrypt_buffer, 0, RBL_UNPACK_RW_BUFFER_SIZE);
		rbl_aes256_decrypt_data(unpack_ctx->decrypt_ctx, unpack_ctx->rw_buffer, unpack_ctx->decrypt_buffer, src_read_rc); // decrypt

		target_write_len = src_read_rc;
		target_remain_len = (unpack_ctx->target_write_total - unpack_ctx->target_write_pos);
		if (target_write_len > target_remain_len)
		{
			target_write_len = target_remain_len;
		}
		target_write_rc = fstorage_write(unpack_ctx->target_interface, unpack_ctx->target_write_pos, unpack_ctx->decrypt_buffer, target_write_len);
		if (target_write_rc < 0)
		{
			ret = RBL_UNPACK_ERR_WRITE;
			goto __exit;
		}
		unpack_ctx->target_write_pos += target_write_rc;

		LOG_RAW("\r\033[K");
		LOG_RAW(STR_SHOW_UNPACK_RATE, unpack_ctx->src_read_pos * 100 / unpack_ctx->src_read_total);
	}
	LOG_RAW("\r\033[K");
	LOG_RAW(STR_SHOW_UNPACK_RATE, unpack_ctx->src_read_pos * 100 / unpack_ctx->src_read_total);
	LOG_RAW("\n");
	
	ret = unpack_ctx->target_write_pos;
	
__exit:
	return ret;
}

#endif /* RBL_UNPACK_USING_AES256 */


#if (RBL_UNPACK_USING_GZIP == 1)

int rbl_unpack_body_output_none_gzip(rbl_unpack_context_t* unpack_ctx)
{
	int ret = 0;
	int target_write_rc = 0;
	int target_write_len = 0;
	int target_remain_len = 0;
	int src_read_rc = 0;
	int src_read_len = 0;
	int src_remain_len = 0;
	
	uint32_t compress_remain = 0;
	//uint32_t compress_block_size = 0;
	uint32_t compress_rc = 0;
	
	LOG_I("unpack none gzip.");
	LOG_RAW(STR_SHOW_UNPACK_RATE, unpack_ctx->src_read_pos * 100 / unpack_ctx->src_read_total);
	while (unpack_ctx->src_read_pos < unpack_ctx->src_read_total)
	{
		src_read_len = RBL_UNPACK_RW_BUFFER_SIZE;
		src_remain_len = (unpack_ctx->src_read_total - unpack_ctx->src_read_pos);
		if (src_read_len > src_remain_len)
		{
			src_read_len = src_remain_len;
		}
		rt_memset(unpack_ctx->rw_buffer, 0, RBL_UNPACK_RW_BUFFER_SIZE);
		src_read_rc = fstorage_read(unpack_ctx->src_interface, sizeof(rbl_header_info_t) + unpack_ctx->src_read_pos, unpack_ctx->rw_buffer, src_read_len);
		if (src_read_rc <= 0)
		{
			ret = RBL_UNPACK_ERR_READ;
			goto __exit;
		}
		unpack_ctx->src_read_pos += src_read_rc;
		
		if( (compress_remain + src_read_rc) > RBL_UNPACK_COMPRESS_BUFFER_SIZE)
		{
			LOG_E("compress buffer len:%d < (%d + %d).", RBL_UNPACK_COMPRESS_BUFFER_SIZE, compress_remain, src_read_rc);
			ret = RBL_UNPACK_ERR_CHECK;
			goto __exit;
		}
		rt_memcpy(unpack_ctx->compress_buffer + compress_remain, unpack_ctx->rw_buffer, src_read_rc); // pkg >> compress
		compress_remain += src_read_rc;
		
		rbl_gzip_decompress_set_in(unpack_ctx->decompress_gzip_ctx, unpack_ctx->compress_buffer, compress_remain);
		while (1)
		{
			compress_rc = rbl_gzip_decompress_data(unpack_ctx->decompress_gzip_ctx, unpack_ctx->rw_buffer, RBL_UNPACK_RW_BUFFER_SIZE);
			if (compress_rc <= 0)
			{
				compress_remain = 0;
				break;
			}
			target_write_len = compress_rc;
			target_remain_len = (unpack_ctx->target_write_total - unpack_ctx->target_write_pos);
			if (target_write_len > target_remain_len)
			{
				target_write_len = target_remain_len;
			}
			target_write_rc = fstorage_write(unpack_ctx->target_interface, unpack_ctx->target_write_pos, unpack_ctx->rw_buffer, target_write_len);
			if (target_write_rc < 0)
			{
				ret = RBL_UNPACK_ERR_WRITE;
				goto __exit;
			}
			unpack_ctx->target_write_pos += target_write_rc;
		}
		LOG_RAW("\r\033[K");
		LOG_RAW(STR_SHOW_UNPACK_RATE, unpack_ctx->src_read_pos * 100 / unpack_ctx->src_read_total);
	}
	LOG_RAW("\r\033[K");
	LOG_RAW(STR_SHOW_UNPACK_RATE, unpack_ctx->src_read_pos * 100 / unpack_ctx->src_read_total);
	LOG_RAW("\n");
	
	ret = unpack_ctx->target_write_pos;
	
__exit:
	return ret;
}
#endif /* RBL_UNPACK_USING_GZIP */


#if (RBL_UNPACK_USING_FASTLZ == 1)

int rbl_unpack_body_output_none_fastlz(rbl_unpack_context_t* unpack_ctx)
{
	int ret = 0;
	int target_write_rc = 0;
	int target_write_len = 0;
	int target_remain_len = 0;
	int src_read_rc = 0;
	int src_read_len = 0;
	int src_remain_len = 0;
	
	uint32_t compress_remain = 0;
	uint32_t compress_block_size = 0;
	uint32_t compress_rc = 0;
	
	LOG_I("unpack none fastlz.");
	LOG_RAW(STR_SHOW_UNPACK_RATE, unpack_ctx->src_read_pos * 100 / unpack_ctx->src_read_total);
	while (unpack_ctx->src_read_pos < unpack_ctx->src_read_total)
	{
		src_read_len = RBL_UNPACK_RW_BUFFER_SIZE;
		src_remain_len = (unpack_ctx->src_read_total - unpack_ctx->src_read_pos);
		if (src_read_len > src_remain_len)
		{
			src_read_len = src_remain_len;
		}
		rt_memset(unpack_ctx->rw_buffer, 0, RBL_UNPACK_RW_BUFFER_SIZE);
		src_read_rc = fstorage_read(unpack_ctx->src_interface, sizeof(rbl_header_info_t) + unpack_ctx->src_read_pos, unpack_ctx->rw_buffer, src_read_len);
		if (src_read_rc <= 0)
		{
			ret = RBL_UNPACK_ERR_READ;
			goto __exit;
		}
		unpack_ctx->src_read_pos += src_read_rc;
		
		if( (compress_remain + src_read_rc) > RBL_UNPACK_COMPRESS_BUFFER_SIZE)
		{
			LOG_E("compress buffer len:%d < (%d + %d).", RBL_UNPACK_COMPRESS_BUFFER_SIZE, compress_remain, src_read_rc);
			ret = RBL_UNPACK_ERR_CHECK;
			goto __exit;
		}
		rt_memcpy(unpack_ctx->compress_buffer + compress_remain, unpack_ctx->rw_buffer, src_read_rc); // pkg >> compress
		compress_remain += src_read_rc;
		while (1)
		{
			compress_block_size = rbl_fastlz_decompress_get_block_size(unpack_ctx->decompress_fz_ctx, unpack_ctx->compress_buffer);
			if (compress_block_size <= 0)
			{
				break;
			}
			if ((compress_block_size + FUPDATE_FASTLZ_HEADER_SIZE) > compress_remain)
			{
				break;
			}
			compress_rc = rbl_fastlz_decompress_data(unpack_ctx->decompress_fz_ctx, unpack_ctx->rw_buffer, RBL_UNPACK_RW_BUFFER_SIZE, &(unpack_ctx->compress_buffer[FUPDATE_FASTLZ_HEADER_SIZE]), compress_block_size);
			if (compress_rc <= 0)
			{
				LOG_E("fastlz decompress failed. code:%d", compress_rc);
				ret = RBL_UNPACK_ERR_WRITE;
				goto __exit;
			}
			target_write_len = compress_rc;
			target_remain_len = (unpack_ctx->target_write_total - unpack_ctx->target_write_pos);
			if (target_write_len > target_remain_len)
			{
				target_write_len = target_remain_len;
			}
			target_write_rc = fstorage_write(unpack_ctx->target_interface, unpack_ctx->target_write_pos, unpack_ctx->rw_buffer, target_write_len);
			if (target_write_rc < 0)
			{
				ret = RBL_UNPACK_ERR_WRITE;
				goto __exit;
			}
			unpack_ctx->target_write_pos += target_write_rc;

			compress_remain = compress_remain - (compress_block_size + FUPDATE_FASTLZ_HEADER_SIZE);
			if (compress_remain > 0)
			{
				rt_memcpy(unpack_ctx->compress_buffer, &(unpack_ctx->compress_buffer[(compress_block_size + FUPDATE_FASTLZ_HEADER_SIZE)]), compress_remain);
			}
		}
		LOG_RAW("\r\033[K");
		LOG_RAW(STR_SHOW_UNPACK_RATE, unpack_ctx->src_read_pos * 100 / unpack_ctx->src_read_total);
	}
	LOG_RAW("\r\033[K");
	LOG_RAW(STR_SHOW_UNPACK_RATE, unpack_ctx->src_read_pos * 100 / unpack_ctx->src_read_total);
	LOG_RAW("\n");
	
	ret = unpack_ctx->target_write_pos;
	
__exit:
	return ret;
}
#endif  /* RBL_UNPACK_USING_FASTLZ */

#if (RBL_UNPACK_USING_QUICKLZ == 1)

int rbl_unpack_body_output_none_quicklz(rbl_unpack_context_t* unpack_ctx)
{
	int ret = 0;
	int target_write_rc = 0;
	int target_write_len = 0;
	int target_remain_len = 0;
	int src_read_rc = 0;
	int src_read_len = 0;
	int src_remain_len = 0;
	
	uint32_t compress_remain = 0;
	uint32_t compress_block_size = 0;
	uint32_t compress_rc = 0;
	
	LOG_I("unpack none quicklz.");
	LOG_RAW(STR_SHOW_UNPACK_RATE, unpack_ctx->src_read_pos * 100 / unpack_ctx->src_read_total);
	while (unpack_ctx->src_read_pos < unpack_ctx->src_read_total)
	{
		src_read_len = RBL_UNPACK_RW_BUFFER_SIZE;
		src_remain_len = (unpack_ctx->src_read_total - unpack_ctx->src_read_pos);
		if (src_read_len > src_remain_len)
		{
			src_read_len = src_remain_len;
		}
		rt_memset(unpack_ctx->rw_buffer, 0, RBL_UNPACK_RW_BUFFER_SIZE);
		src_read_rc = fstorage_read(unpack_ctx->src_interface, sizeof(rbl_header_info_t) + unpack_ctx->src_read_pos, unpack_ctx->rw_buffer, src_read_len);
		if (src_read_rc <= 0)
		{
			ret = RBL_UNPACK_ERR_READ;
			goto __exit;
		}
		unpack_ctx->src_read_pos += src_read_rc;
		
		if( (compress_remain + src_read_rc) > RBL_UNPACK_COMPRESS_BUFFER_SIZE)
		{
			LOG_E("compress buffer len:%d < (%d + %d).", RBL_UNPACK_COMPRESS_BUFFER_SIZE, compress_remain, src_read_rc);
			ret = RBL_UNPACK_ERR_CHECK;
			goto __exit;
		}
		rt_memcpy(unpack_ctx->compress_buffer + compress_remain, unpack_ctx->rw_buffer, src_read_rc); // rw_buffer >> compress_buffer
		compress_remain += src_read_rc;
		while (1)
		{
			compress_block_size = rbl_quicklz_decompress_get_block_size(unpack_ctx->decompress_qz_ctx,  unpack_ctx->compress_buffer);
			if (compress_block_size <= 0)
			{
				break;
			}
			if ((compress_block_size + FUPDATE_QUICKLZ_HEADER_SIZE) > compress_remain)
			{
				break;
			}
			compress_rc = rbl_quicklz_decompress_data(unpack_ctx->decompress_qz_ctx, unpack_ctx->rw_buffer, &(unpack_ctx->compress_buffer[FUPDATE_QUICKLZ_HEADER_SIZE]));
			if (compress_rc <= 0)
			{
				LOG_E("quicklz decompress failed. code:%d", compress_rc);
				ret = RBL_UNPACK_ERR_WRITE;
				goto __exit;
			}
			target_write_len = compress_rc;
			target_remain_len = (unpack_ctx->target_write_total - unpack_ctx->target_write_pos);
			if (target_write_len > target_remain_len)
			{
				target_write_len = target_remain_len;
			}
			target_write_rc = fstorage_write(unpack_ctx->target_interface, unpack_ctx->target_write_pos, unpack_ctx->rw_buffer, target_write_len);
			if (target_write_rc < 0)
			{
				ret = RBL_UNPACK_ERR_WRITE;
				goto __exit;
			}
			unpack_ctx->target_write_pos += target_write_rc;

			compress_remain = compress_remain - (compress_block_size + FUPDATE_QUICKLZ_HEADER_SIZE);
			if (compress_remain > 0)
			{
				rt_memcpy(unpack_ctx->compress_buffer, &(unpack_ctx->compress_buffer[(compress_block_size + FUPDATE_QUICKLZ_HEADER_SIZE)]), compress_remain);
			}
		}
		LOG_RAW("\r\033[K");
		LOG_RAW(STR_SHOW_UNPACK_RATE, unpack_ctx->src_read_pos * 100 / unpack_ctx->src_read_total);
	}
	LOG_RAW("\r\033[K");
	LOG_RAW(STR_SHOW_UNPACK_RATE, unpack_ctx->src_read_pos * 100 / unpack_ctx->src_read_total);
	LOG_RAW("\n");
	
	ret = unpack_ctx->target_write_pos;
	
__exit:
	return ret;
}
#endif /* RBL_UNPACK_USING_QUICKLZ */

int rbl_unpack_body_output_none_none(rbl_unpack_context_t* unpack_ctx)
{
	int ret = 0;
	int target_write_rc = 0;
	int target_write_len = 0;
	int target_remain_len = 0;
	int src_read_rc = 0;
	int src_read_len = 0;
	int src_remain_len = 0;
	
	LOG_I("unpack none none.");
	LOG_RAW(STR_SHOW_UNPACK_RATE, unpack_ctx->src_read_pos * 100 / unpack_ctx->src_read_total);
	while (unpack_ctx->src_read_pos < unpack_ctx->src_read_total)
	{
		src_read_len = RBL_UNPACK_RW_BUFFER_SIZE;
		src_remain_len = (unpack_ctx->src_read_total - unpack_ctx->src_read_pos);
		if (src_read_len > src_remain_len)
		{
			src_read_len = src_remain_len;
		}
		rt_memset(unpack_ctx->rw_buffer, 0, RBL_UNPACK_RW_BUFFER_SIZE);
		src_read_rc = fstorage_read(unpack_ctx->src_interface, sizeof(rbl_header_info_t) + unpack_ctx->src_read_pos, unpack_ctx->rw_buffer, src_read_len);
		if (src_read_rc <= 0)
		{
			ret = RBL_UNPACK_ERR_READ;
			goto __exit;
		}
		unpack_ctx->src_read_pos += src_read_rc;  //read pos
		
		target_write_len = src_read_rc;
		target_remain_len = (unpack_ctx->target_write_total - unpack_ctx->target_write_pos);
		if (target_write_len > target_remain_len)
		{
			target_write_len = target_remain_len;
		}
		target_write_rc = fstorage_write(unpack_ctx->target_interface, unpack_ctx->target_write_pos , unpack_ctx->rw_buffer, target_write_len);
		if (target_write_rc < 0)
		{
			ret = RBL_UNPACK_ERR_WRITE;
			goto __exit;
		}
		unpack_ctx->target_write_pos += target_write_rc;  //write pos

		LOG_RAW("\r\033[K");
		LOG_RAW(STR_SHOW_UNPACK_RATE, unpack_ctx->src_read_pos * 100 / unpack_ctx->src_read_total);
	}
	LOG_RAW("\r\033[K");
	LOG_RAW(STR_SHOW_UNPACK_RATE, unpack_ctx->src_read_pos * 100 / unpack_ctx->src_read_total);
	LOG_RAW("\n");
	
	ret = unpack_ctx->target_write_pos;
	
__exit:
	return ret;
}

int rbl_unpack_body_output(rbl_unpack_context_t* unpack_ctx)
{
	int ret = 0;
	uint16_t crypt_type = (unpack_ctx->rbl_header.fota_algo & RBL_CRYPT_STAT_MASK);
	uint16_t compress_type = (unpack_ctx->rbl_header.fota_algo & RBL_CMPRS_STAT_MASK);

	uint32_t tick_start = 0;
	uint32_t tick_end = 0;
	uint32_t tick_speed = 0;
	
	unpack_ctx->src_read_total = unpack_ctx->rbl_header.pkg_size;
	unpack_ctx->target_write_total = unpack_ctx->rbl_header.raw_size;

	LOG_I("erase len: 0x%08X.",RT_ALIGN(unpack_ctx->target_write_total,4));
	tick_start = rt_tick_get();
	ret = fstorage_erase(unpack_ctx->target_interface, 0, RT_ALIGN(unpack_ctx->target_write_total,4) );
	if (ret < 0)
	{
		goto __exit;
	}
	tick_end = rt_tick_get();
	tick_speed = ((RT_ALIGN(unpack_ctx->target_write_total,4) * 1000U) / (1+ (tick_end - tick_start)));
	LOG_I("erase used: %d ms, speed: %d bytes/s.",(tick_end - tick_start), tick_speed);
	
	LOG_I("unpack len: 0x%08X.", unpack_ctx->target_write_total);
	tick_start = rt_tick_get();
	// AES256 encryption
	if (crypt_type == RBL_CRYPT_ALGO_AES256)
	{
#if (RBL_UNPACK_USING_AES256 == 1)
		/* compress option */
		if (compress_type == RBL_CMPRS_ALGO_GZIP)
		{
		#if (RBL_UNPACK_USING_GZIP == 1)
			ret = rbl_unpack_body_output_aes256_gzip(unpack_ctx);
		#endif
		}else
		if (compress_type == RBL_CMPRS_ALGO_QUICKLZ)
		{
		#if (RBL_UNPACK_USING_QUICKLZ == 1)
			ret = rbl_unpack_body_output_aes256_quicklz(unpack_ctx);
		#endif
		}else
		if (compress_type == RBL_CMPRS_ALGO_FASTLZ)
		{
		#if (RBL_UNPACK_USING_FASTLZ == 1)
			ret = rbl_unpack_body_output_aes256_fastlz(unpack_ctx);
		#endif
		}else
		{
			ret = rbl_unpack_body_output_aes256_none(unpack_ctx);
		}
#endif /* RBL_UNPACK_USING_AES256 */
	}else
	{
		/* compress option */
		if (compress_type == RBL_CMPRS_ALGO_GZIP)
		{
		#if (RBL_UNPACK_USING_GZIP == 1)
			ret = rbl_unpack_body_output_none_gzip(unpack_ctx);
		#endif
		}else
		if (compress_type == RBL_CMPRS_ALGO_QUICKLZ)
		{
		#if (RBL_UNPACK_USING_QUICKLZ == 1)
			ret = rbl_unpack_body_output_none_quicklz(unpack_ctx);
		#endif
		}else
		if (compress_type == RBL_CMPRS_ALGO_FASTLZ)
		{
		#if (RBL_UNPACK_USING_FASTLZ == 1)
			ret = rbl_unpack_body_output_none_fastlz(unpack_ctx);
		#endif
		}else
		{
			ret = rbl_unpack_body_output_none_none(unpack_ctx);
		}
	}
	tick_end = rt_tick_get();
	LOG_I("unpack read : %08d / %08d bytes.", unpack_ctx->src_read_pos, unpack_ctx->src_read_total);
	LOG_I("unpack write: %08d / %08d bytes.", unpack_ctx->target_write_pos, unpack_ctx->target_write_total);
	tick_speed = ((unpack_ctx->target_write_pos * 1000U) / (1+ (tick_end - tick_start)));
	LOG_I("unpack used: %d ms, speed: %d bytes/s.",(tick_end - tick_start), tick_speed);
	
__exit:
	LOG_I("unpack end: %d.",ret);
	return ret;
}


#endif /* FBOOT_USING_FUPDATE */
